#ifndef DEVICES_H_INCLUDED
#define DEVICES_H_INCLUDED


#include <ctype.h>
#include <linux/types.h>
#include <linux/virtio_ring.h>
#include <stdbool.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>

#include "Drivers.h"

typedef unsigned long long u64;
typedef uint32_t u32;
typedef uint16_t u16;
typedef uint8_t u8;

//Lista de dispositivos del guest
struct device_list devices;

//Lista de dispositivos
struct device_list {
	// Numero de interrupción
	unsigned int next_irq;

	//Identificador del dispositivo
	unsigned int device_num;

	//Descriptor de la pagina del dispositivo
	u8 *descpage;

	//Lista de dispositivos
	struct device *dev;

	//Puntero al ultimos dispositivo
	struct device *lastdev;
};


//Describe un dispositivo
struct device {
	//Lista de Dispositivos
	struct device *next;

	/* Descriptor del Dispositivo. */
	struct device_desc *desc;

	//No confiar en la desfcripción pued ealterarse al empezar la ejecucción
	unsigned int feature_len;
	unsigned int num_vq;

	const char *name;

	//Colas que apuntan al dispositivo
	struct virtqueue *vq;

	//Esta operacional
	bool running;

	//HAce una interrupción al estar vacio
	bool irq_on_empty;

	//Puntero a datos privados del dispositivo
	void *priv;
};

//Cola de éticipnes que tiene el dispositivo
struct virtqueue {
	struct virtqueue *next;

	//A que dispositivo pertenece esta cola
	struct device *dev;

	//
	struct vqconfig config;

	//Buffers
	struct vring vring;

	//Ultimo indice antendido
	u16 last_avail_idx;

	//Desde la ultima interrupcion fue pedida, cuantas se encuentran pendientes
	unsigned int pending_used;

	//Notificaciones del huesped
	int eventfd;

	//Funcion que atiende esta cola
	void (*service)(struct virtqueue *vq);
	pid_t thread;
};

/*
 *MAneja la separción de los threads paa los diferentes dispositivos
 */
#define wmb() __asm__ __volatile__("" : : : "memory")
#define mb() __asm__ __volatile__("" : : : "memory")

/*
 * Convierte un iovec a un tipo determinado
 */
#define convert(iov, type) \
	((type *)_convert((iov), sizeof(type), __alignof__(type), #type))

void *_convert(struct iovec *iov, size_t size, size_t align,const char *name);

/* Wrapper para el último index disponible. */
#define last_avail(vq)	((vq)->last_avail_idx)

/*
 * Conversión a little endian por seguridad
 */
#define cpu_to_le16(v16) (v16)
#define cpu_to_le32(v32) (v32)
#define cpu_to_le64(v64) (v64)
#define le16_to_cpu(v16) (v16)
#define le32_to_cpu(v32) (v32)
#define le64_to_cpu(v64) (v64)

/* Determina si un elemeno iovec esta o no vacio */
bool iov_empty(const struct iovec iov[], unsigned int num_iov);

/* Consume una longitud len de bytes del dispositivo. */
void iov_consume(struct iovec iov[], unsigned num_iov, unsigned len);

/* Obtiene la mascara de las caracterísitcas d elos dispositivos */
u8 *get_feature_bits(struct device *dev);

#endif
